package com.kit.personalkit.controller;

import cn.hutool.json.JSONUtil;
import com.kit.personalkit.common.JsonResponseBody;
import com.kit.personalkit.common.PageUtils;
import com.kit.personalkit.common.ResponseStatus;
import com.kit.personalkit.mapper.AudioMapper;
import com.kit.personalkit.pojo.dto.SearchAudioByPageDTO;
import com.kit.personalkit.pojo.entity.Audio;
import com.kit.personalkit.security.CustomUserDetails;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import ws.schild.jave.Encoder;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 音频控制器:
 * 用来处理客户端请求并返回响应结果的类
 */
@RestController
@RequestMapping("/audios/")
public class AudioController {
    //创建视频存放目录
    private static File videoDir;
    //创建音频存放目录
    private static File audioDir;

    static {
        //创建视频存放目录
        videoDir = new File(System.getProperty("user.dir") + "/videosMp4");
        if (!videoDir.exists()) {
            videoDir.mkdirs();
        }

        //创建音频存放目录
        audioDir = new File(System.getProperty("user.dir") + "/audiosMp3");
        if (!audioDir.exists()) {
            audioDir.mkdirs();
        }
    }

    @Autowired(required = false)
    private AudioMapper audioMapper;

    /**
     * 上传视频文件并实现Mp4与Mp3文件的转换
     */
    @PostMapping("/videosUpload")
    public JsonResponseBody uploadVideo(@RequestParam("files") MultipartFile[] videoFiles,
                                        @RequestParam String audioFormat,
                                        @AuthenticationPrincipal CustomUserDetails cud) throws Exception {
        if (videoFiles != null && videoFiles.length != 0) {
            for (MultipartFile videoFile : videoFiles) {
                if (videoFile.getSize() > 500 * 1024 * 1024) {
                    return new JsonResponseBody(ResponseStatus.UPLOAD_SIZE_ERROR);
                }

                // 上传视频文件 将视频文件存入本地的视频文件夹videosMp4中
                String videoName = videoFile.getOriginalFilename();
                String ext = FilenameUtils.getExtension(videoName);
                String name = FilenameUtils.getBaseName(videoName);

                //判断数据库中是否已有重名的音频文件
                Audio audio = audioMapper.selectAudioByName(name);
                if(audio != null) {
                    return new JsonResponseBody(ResponseStatus.AUDIO_ALREADY_EXISTS);
                }

                if (!ext.matches("(?i)(mp4|avi|flv|mov|mpeg)$")) { // 只允许指定的视频格式
                    return new JsonResponseBody(ResponseStatus.UPLOAD_TYPE_ERROR);
                }
                File dest = new File(videoDir, videoName);
                videoFile.transferTo(dest);

                // 音频转换 将视频文件夹videosMp4中的视频文件转为音频文件后再存入audiosMp3文件夹中
                String prefix = FilenameUtils.getBaseName(videoName);// 获取上传文件名的基本名称（不包括扩展名）
                String audioName = prefix + "." + audioFormat;

                String userFilePath = audioDir.getPath() + "/" + cud.getUserId();
                File userAudioFile = new File(userFilePath);
                if(!userAudioFile.exists()) {
                    userAudioFile.mkdirs();
                }

                String inputPath = videoDir.getPath() + "/" + videoName;
                String outputPath = userAudioFile.getPath() + "\\" + audioName;

                // 设置需要转换的音频属性
                AudioAttributes audioProperty = new AudioAttributes();
                audioProperty.setChannels(new Integer(2)); // 声道数为2，双声道：立体声

                if (audioFormat.equalsIgnoreCase("mp3")) {
                    // 设置MP3格式的编解码器属性
                    audioProperty.setCodec("libmp3lame"); // 编解码器
                    audioProperty.setBitRate(new Integer(256000)); // 比特率
                } else if (audioFormat.equalsIgnoreCase("wav")) {
                    // WAV格式为无损音质，不需要设置编解码器属性
                    audioProperty.setBitRate(null);
                } else if (audioFormat.equalsIgnoreCase("ogg")) {
                    // 设置OGG格式的编解码器属性
                    audioProperty.setCodec("libvorbis");
                    audioProperty.setQuality((int) 0.5); // 设置质量参数，OGG的质量参数在 0.0 到 1.0 之间
                } else if (audioFormat.equalsIgnoreCase("flac")) {
                    // 设置OGG格式的编解码器属性
                    audioProperty.setCodec("flac");
                }
                // 大多数音频的采样率通常为44100
                audioProperty.setSamplingRate(new Integer(44100));

                // 创建编码器并设置编码属性
                EncodingAttributes attrs = new EncodingAttributes();
                attrs.setInputFormat(ext);  // 输入格式
                attrs.setOutputFormat(audioFormat);  // 输出格式
                attrs.setAudioAttributes(audioProperty);

                // 创建编码器对象并进行编码
                Encoder encoder = new Encoder();
                encoder.encode(new MultimediaObject(new File(inputPath)), new File(outputPath), attrs);

                //获取当前音频文件信息并将其存入数据库
                File audioFile = new File(outputPath);

                Audio a = new Audio();
                a.setAudioName(prefix);
                a.setAudioPath(outputPath);
                a.setAudioSize((int) audioFile.length());
                a.setAudioFormat(FilenameUtils.getExtension(audioFile.getName()));
                a.setUploadTime(new Date());
                a.setUserId(cud.getUserId());
                audioMapper.insertAudioFile(a);


                //将视频转成音频之后将该视频文件删除
                File video = new File(inputPath);
                if (video.exists()) {
                    video.delete();
                }
            }

            return new JsonResponseBody(ResponseStatus.UPLOAD_SUCCESS);
        } else {
            return new JsonResponseBody(ResponseStatus.UPLOAD_FAIL);
        }
    }

    /**
     * 下载音频文件
     */
    @GetMapping("/{index}/downloadAudioFile")
    public void downloadAudio(@PathVariable Integer index, HttpServletResponse response,
                              @AuthenticationPrincipal CustomUserDetails cud) throws IOException {
        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);

        // 获取要下载的文件路径
        String path = audioDir.getPath() + "/" + cud.getUserId();
        File file = new File(path);
        File[] audiosFile = file.listFiles();

        //下载多个文件时使用压缩包打包
        if (index <= 0) {
            //创建一个临时压缩文件,用于将所有音频临时保存到该压缩文件中,
            //因为一个http响应头只能发送一个文件,即只能下载一个文件,而通过该临时压缩文件可以同时下载多个文件
            File zipFile = File.createTempFile("audios", ".zip");
            ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
            for (File audio : audiosFile) {
                // 从文件系统中读取文件内容
                Path audioFilePath = Paths.get(audio.getPath());
                byte[] data = Files.readAllBytes(audioFilePath);

                //将文件添加到压缩文件中
                ZipEntry entry = new ZipEntry(audio.getName());
                zipOut.putNextEntry(entry);
                zipOut.write(data);
                zipOut.closeEntry();
            }

            //关闭压缩文件流
            zipOut.close();

            //设置响应文件
            response.setHeader("Content-Disposition", "attachment; filename= audios.zip");
            response.setContentType("application/zip");

            // 从临时压缩文件中读取压缩文件数据,并写出到响应输出流
            Path zipFilPath = zipFile.toPath();
            byte[] zipData = Files.readAllBytes(zipFilPath);
            response.setContentLength(zipData.length);
            response.getOutputStream().write(zipData);

            //删除临时文件
            zipFile.delete();
        } else {
            Audio audio = audioMapper.selectById(index);
            String audioPath = audio.getAudioPath();

            // 从文件系统中读取文件内容
            Path audioFilePath = Paths.get(audioPath);
            byte[] data = Files.readAllBytes(audioFilePath);

            // 设置响应头
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(String.valueOf(audioFilePath.getFileName()), "utf8"));


            // 写出文件数据
            response.setContentLength(data.length);
            response.getOutputStream().write(data);
            response.getOutputStream().flush();
        }
    }

    /**
     * 删除单个音频文件
     */
    @PostMapping("{audioId}/deleteAudioById")
    public JsonResponseBody deleteAudioById(@PathVariable Integer audioId) {
        Audio audio = audioMapper.selectById(audioId);
        String filePath = audio.getAudioPath();
        File audioFile = new File(filePath);

        audioMapper.deleteById(audioId);
        if (audioFile.exists()) {
            audioFile.delete();
        }

        return new JsonResponseBody(ResponseStatus.DELETE_SUCCESS);
    }

    /**
     * 清空数据库和audioMp3文件夹
     */
    @PostMapping("clear")
    public JsonResponseBody clear(@AuthenticationPrincipal CustomUserDetails cud) {
        audioMapper.deleteAllAudiosById(cud.getUserId());
        String audioPath = audioDir.getPath() + "/" + cud.getUserId();
        File audioFile = new File(audioPath);
        File[] audios = audioFile.listFiles();
        for (File audio : audios) {
            if (audio.exists()) {
                audio.delete();
            }
        }

        return new JsonResponseBody(ResponseStatus.DELETE_SUCCESS);
    }

    /**
     * 查询所有音频文件
     */
    @PostMapping("selectAll")
    public JsonResponseBody selectAll(@RequestBody SearchAudioByPageDTO form ,
                                      @AuthenticationPrincipal CustomUserDetails cud) {
        if (cud == null){
            return new JsonResponseBody(ResponseStatus.NOT_LOGIN);
        }
        HashMap param = JSONUtil.parse(form).toBean(HashMap.class);
        int pageIndex = form.getPageIndex();
        int length = form.getPageSize();
        int start = (pageIndex - 1) * length;
        Integer userId = cud.getUserId();
        param.put("start",start);
        param.put("length",length);
        param.put("userId",userId);
        // 根据用户id从数据库中查询出相应用户的所有音频并以ArrayList集合，集合类型为HashMap的形式返回
        ArrayList<HashMap> list = audioMapper.selectAllAudio(param);

        // 创建一个新的ArrayList集合集合类型为HashMap用来将音频我呢见大小转为B,KB,MB的形式返回给客户端
        ArrayList<HashMap> audioSHOWList = new ArrayList<>();
        for (HashMap<String, Object> audioMap : list) {
            Set<Map.Entry<String, Object>> audioSet = audioMap.entrySet();

            // 创建一个Map用于接收每一个遍历并转换音频大小的音频字段
            HashMap<String, Object> audioShowMap = new HashMap<>();
            for (Map.Entry<String, Object> audio : audioSet) {
                if (audio.getValue() instanceof Long) {
                    //将文件长度转为B,KB,MB,GB的形式
                    System.out.println(audio.getValue());
                    //此处必须将每一个遍历的音频大小value值强转为Long，因为由于泛型擦除会无法识别该value的真实数据类型，所以必须强转
                    //并通过doubleValue()方法将该Long类型的值转为double类型,便于计算
                    double audioSize = ((Long) audio.getValue()).doubleValue();
                    int index = 0;
                    String[] suffix = {"B", "KB", "MB", "GB"};
                    while (audioSize >= 1024 && index < suffix.length) {
                        audioSize /= 1024;
                        index++;
                    }
                    //将转换形式后的音频大小保留两位小数并存入audioShowMap中
                    String size = String.format("%.2f", audioSize) + suffix[index];
                    audioShowMap.put(audio.getKey(), size);
                } else {
                    //不是音频大小的字段直接存入audioShowMap中
                    audioShowMap.put(audio.getKey(), audio.getValue());
                }
            }

            //将转化内后接收每一个音频文件的audioShowMap存入audioSHOWList中
            audioSHOWList.add(audioShowMap);
        }

        Long count = audioMapper.selectCount(param);
        PageUtils pageUtils = new PageUtils(audioSHOWList,count,start,length);
        return new JsonResponseBody(ResponseStatus.SUCCESS,pageUtils);
    }
}
